home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / lib / python2.5 / zipfile.pyc (.txt) < prev   
Python Compiled Bytecode  |  2008-10-29  |  26KB  |  858 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. '''
  5. Read and write ZIP files.
  6. '''
  7. import struct
  8. import os
  9. import time
  10. import sys
  11. import binascii
  12. import cStringIO
  13.  
  14. try:
  15.     import zlib
  16. except ImportError:
  17.     zlib = None
  18.  
  19. __all__ = [
  20.     'BadZipfile',
  21.     'error',
  22.     'ZIP_STORED',
  23.     'ZIP_DEFLATED',
  24.     'is_zipfile',
  25.     'ZipInfo',
  26.     'ZipFile',
  27.     'PyZipFile',
  28.     'LargeZipFile']
  29.  
  30. class BadZipfile(Exception):
  31.     pass
  32.  
  33.  
  34. class LargeZipFile(Exception):
  35.     '''
  36.     Raised when writing a zipfile, the zipfile requires ZIP64 extensions
  37.     and those extensions are disabled.
  38.     '''
  39.     pass
  40.  
  41. error = BadZipfile
  42. ZIP64_LIMIT = (1 << 31) - 1
  43. ZIP_STORED = 0
  44. ZIP_DEFLATED = 8
  45. structEndArchive = '<4s4H2LH'
  46. stringEndArchive = 'PK\x05\x06'
  47. structCentralDir = '<4s4B4HlLL5HLL'
  48. stringCentralDir = 'PK\x01\x02'
  49. structFileHeader = '<4s2B4HlLL2H'
  50. stringFileHeader = 'PK\x03\x04'
  51. structEndArchive64Locator = '<4slql'
  52. stringEndArchive64Locator = 'PK\x06\x07'
  53. structEndArchive64 = '<4sqhhllqqqq'
  54. stringEndArchive64 = 'PK\x06\x06'
  55. _CD_SIGNATURE = 0
  56. _CD_CREATE_VERSION = 1
  57. _CD_CREATE_SYSTEM = 2
  58. _CD_EXTRACT_VERSION = 3
  59. _CD_EXTRACT_SYSTEM = 4
  60. _CD_FLAG_BITS = 5
  61. _CD_COMPRESS_TYPE = 6
  62. _CD_TIME = 7
  63. _CD_DATE = 8
  64. _CD_CRC = 9
  65. _CD_COMPRESSED_SIZE = 10
  66. _CD_UNCOMPRESSED_SIZE = 11
  67. _CD_FILENAME_LENGTH = 12
  68. _CD_EXTRA_FIELD_LENGTH = 13
  69. _CD_COMMENT_LENGTH = 14
  70. _CD_DISK_NUMBER_START = 15
  71. _CD_INTERNAL_FILE_ATTRIBUTES = 16
  72. _CD_EXTERNAL_FILE_ATTRIBUTES = 17
  73. _CD_LOCAL_HEADER_OFFSET = 18
  74. _FH_SIGNATURE = 0
  75. _FH_EXTRACT_VERSION = 1
  76. _FH_EXTRACT_SYSTEM = 2
  77. _FH_GENERAL_PURPOSE_FLAG_BITS = 3
  78. _FH_COMPRESSION_METHOD = 4
  79. _FH_LAST_MOD_TIME = 5
  80. _FH_LAST_MOD_DATE = 6
  81. _FH_CRC = 7
  82. _FH_COMPRESSED_SIZE = 8
  83. _FH_UNCOMPRESSED_SIZE = 9
  84. _FH_FILENAME_LENGTH = 10
  85. _FH_EXTRA_FIELD_LENGTH = 11
  86.  
  87. def is_zipfile(filename):
  88.     '''Quickly see if file is a ZIP file by checking the magic number.'''
  89.     
  90.     try:
  91.         fpin = open(filename, 'rb')
  92.         endrec = _EndRecData(fpin)
  93.         fpin.close()
  94.         if endrec:
  95.             return True
  96.     except IOError:
  97.         pass
  98.  
  99.     return False
  100.  
  101.  
  102. def _EndRecData64(fpin, offset, endrec):
  103.     '''
  104.     Read the ZIP64 end-of-archive records and use that to update endrec
  105.     '''
  106.     locatorSize = struct.calcsize(structEndArchive64Locator)
  107.     fpin.seek(offset - locatorSize, 2)
  108.     data = fpin.read(locatorSize)
  109.     (sig, diskno, reloff, disks) = struct.unpack(structEndArchive64Locator, data)
  110.     if sig != stringEndArchive64Locator:
  111.         return endrec
  112.     
  113.     if diskno != 0 or disks != 1:
  114.         raise BadZipfile('zipfiles that span multiple disks are not supported')
  115.     
  116.     endArchiveSize = struct.calcsize(structEndArchive64)
  117.     fpin.seek(offset - locatorSize - endArchiveSize, 2)
  118.     data = fpin.read(endArchiveSize)
  119.     (sig, sz, create_version, read_version, disk_num, disk_dir, dircount, dircount2, dirsize, diroffset) = struct.unpack(structEndArchive64, data)
  120.     if sig != stringEndArchive64:
  121.         return endrec
  122.     
  123.     endrec[1] = disk_num
  124.     endrec[2] = disk_dir
  125.     endrec[3] = dircount
  126.     endrec[4] = dircount2
  127.     endrec[5] = dirsize
  128.     endrec[6] = diroffset
  129.     return endrec
  130.  
  131.  
  132. def _EndRecData(fpin):
  133.     '''Return data from the "End of Central Directory" record, or None.
  134.  
  135.     The data is a list of the nine items in the ZIP "End of central dir"
  136.     record followed by a tenth item, the file seek offset of this record.'''
  137.     fpin.seek(-22, 2)
  138.     filesize = fpin.tell() + 22
  139.     data = fpin.read()
  140.     if data[0:4] == stringEndArchive and data[-2:] == '\x00\x00':
  141.         endrec = struct.unpack(structEndArchive, data)
  142.         endrec = list(endrec)
  143.         endrec.append('')
  144.         endrec.append(filesize - 22)
  145.         if endrec[-4] == -1 or endrec[-4] == 0xFFFFFFFFL:
  146.             return _EndRecData64(fpin, -22, endrec)
  147.         
  148.         return endrec
  149.     
  150.     END_BLOCK = min(filesize, 4096)
  151.     fpin.seek(filesize - END_BLOCK, 0)
  152.     data = fpin.read()
  153.     start = data.rfind(stringEndArchive)
  154.     if start >= 0:
  155.         endrec = struct.unpack(structEndArchive, data[start:start + 22])
  156.         endrec = list(endrec)
  157.         comment = data[start + 22:]
  158.         if endrec[7] == len(comment):
  159.             endrec.append(comment)
  160.             endrec.append((filesize - END_BLOCK) + start)
  161.             if endrec[-4] == -1 or endrec[-4] == 0xFFFFFFFFL:
  162.                 return _EndRecData64(fpin, -END_BLOCK + start, endrec)
  163.             
  164.             return endrec
  165.         
  166.     
  167.  
  168.  
  169. class ZipInfo(object):
  170.     '''Class with attributes describing each file in the ZIP archive.'''
  171.     __slots__ = ('orig_filename', 'filename', 'date_time', 'compress_type', 'comment', 'extra', 'create_system', 'create_version', 'extract_version', 'reserved', 'flag_bits', 'volume', 'internal_attr', 'external_attr', 'header_offset', 'CRC', 'compress_size', 'file_size')
  172.     
  173.     def __init__(self, filename = 'NoName', date_time = (1980, 1, 1, 0, 0, 0)):
  174.         self.orig_filename = filename
  175.         null_byte = filename.find(chr(0))
  176.         if null_byte >= 0:
  177.             filename = filename[0:null_byte]
  178.         
  179.         if os.sep != '/' and os.sep in filename:
  180.             filename = filename.replace(os.sep, '/')
  181.         
  182.         self.filename = filename
  183.         self.date_time = date_time
  184.         self.compress_type = ZIP_STORED
  185.         self.comment = ''
  186.         self.extra = ''
  187.         if sys.platform == 'win32':
  188.             self.create_system = 0
  189.         else:
  190.             self.create_system = 3
  191.         self.create_version = 20
  192.         self.extract_version = 20
  193.         self.reserved = 0
  194.         self.flag_bits = 0
  195.         self.volume = 0
  196.         self.internal_attr = 0
  197.         self.external_attr = 0
  198.  
  199.     
  200.     def FileHeader(self):
  201.         '''Return the per-file header as a string.'''
  202.         dt = self.date_time
  203.         dosdate = dt[0] - 1980 << 9 | dt[1] << 5 | dt[2]
  204.         dostime = dt[3] << 11 | dt[4] << 5 | dt[5] // 2
  205.         if self.flag_bits & 8:
  206.             CRC = compress_size = file_size = 0
  207.         else:
  208.             CRC = self.CRC
  209.             compress_size = self.compress_size
  210.             file_size = self.file_size
  211.         extra = self.extra
  212.         if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT:
  213.             fmt = '<hhqq'
  214.             extra = extra + struct.pack(fmt, 1, struct.calcsize(fmt) - 4, file_size, compress_size)
  215.             file_size = 0xFFFFFFFFL
  216.             compress_size = 0xFFFFFFFFL
  217.             self.extract_version = max(45, self.extract_version)
  218.             self.create_version = max(45, self.extract_version)
  219.         
  220.         header = struct.pack(structFileHeader, stringFileHeader, self.extract_version, self.reserved, self.flag_bits, self.compress_type, dostime, dosdate, CRC, compress_size, file_size, len(self.filename), len(extra))
  221.         return header + self.filename + extra
  222.  
  223.     
  224.     def _decodeExtra(self):
  225.         extra = self.extra
  226.         unpack = struct.unpack
  227.         while extra:
  228.             (tp, ln) = unpack('<hh', extra[:4])
  229.             if tp == 1:
  230.                 if ln >= 24:
  231.                     counts = unpack('<qqq', extra[4:28])
  232.                 elif ln == 16:
  233.                     counts = unpack('<qq', extra[4:20])
  234.                 elif ln == 8:
  235.                     counts = unpack('<q', extra[4:12])
  236.                 elif ln == 0:
  237.                     counts = ()
  238.                 else:
  239.                     raise RuntimeError, 'Corrupt extra field %s' % (ln,)
  240.                 idx = 0
  241.                 if self.file_size == -1 or self.file_size == 0xFFFFFFFFL:
  242.                     self.file_size = counts[idx]
  243.                     idx += 1
  244.                 
  245.                 if self.compress_size == -1 or self.compress_size == 0xFFFFFFFFL:
  246.                     self.compress_size = counts[idx]
  247.                     idx += 1
  248.                 
  249.                 if self.header_offset == -1 or self.header_offset == 0xFFFFFFFFL:
  250.                     old = self.header_offset
  251.                     self.header_offset = counts[idx]
  252.                     idx += 1
  253.                 
  254.             
  255.             extra = extra[ln + 4:]
  256.  
  257.  
  258.  
  259. class ZipFile:
  260.     ''' Class with methods to open, read, write, close, list zip files.
  261.  
  262.     z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True)
  263.  
  264.     file: Either the path to the file, or a file-like object.
  265.           If it is a path, the file will be opened and closed by ZipFile.
  266.     mode: The mode can be either read "r", write "w" or append "a".
  267.     compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
  268.     allowZip64: if True ZipFile will create files with ZIP64 extensions when
  269.                 needed, otherwise it will raise an exception when this would
  270.                 be necessary.
  271.  
  272.     '''
  273.     fp = None
  274.     
  275.     def __init__(self, file, mode = 'r', compression = ZIP_STORED, allowZip64 = False):
  276.         '''Open the ZIP file with mode read "r", write "w" or append "a".'''
  277.         self._allowZip64 = allowZip64
  278.         self._didModify = False
  279.         if compression == ZIP_STORED:
  280.             pass
  281.         elif compression == ZIP_DEFLATED:
  282.             if not zlib:
  283.                 raise RuntimeError, 'Compression requires the (missing) zlib module'
  284.             
  285.         else:
  286.             raise RuntimeError, 'That compression method is not supported'
  287.         self.debug = 0
  288.         self.NameToInfo = { }
  289.         self.filelist = []
  290.         self.compression = compression
  291.         self.mode = key = mode.replace('b', '')[0]
  292.         if isinstance(file, basestring):
  293.             self._filePassed = 0
  294.             self.filename = file
  295.             modeDict = {
  296.                 'r': 'rb',
  297.                 'w': 'wb',
  298.                 'a': 'r+b' }
  299.             self.fp = open(file, modeDict[mode])
  300.         else:
  301.             self._filePassed = 1
  302.             self.fp = file
  303.             self.filename = getattr(file, 'name', None)
  304.         if key == 'r':
  305.             self._GetContents()
  306.         elif key == 'w':
  307.             pass
  308.         elif key == 'a':
  309.             
  310.             try:
  311.                 self._RealGetContents()
  312.                 self.fp.seek(self.start_dir, 0)
  313.             except BadZipfile:
  314.                 self.fp.seek(0, 2)
  315.             except:
  316.                 None<EXCEPTION MATCH>BadZipfile
  317.             
  318.  
  319.         None<EXCEPTION MATCH>BadZipfile
  320.         if not self._filePassed:
  321.             self.fp.close()
  322.             self.fp = None
  323.         
  324.         raise RuntimeError, 'Mode must be "r", "w" or "a"'
  325.  
  326.     
  327.     def _GetContents(self):
  328.         '''Read the directory, making sure we close the file if the format
  329.         is bad.'''
  330.         
  331.         try:
  332.             self._RealGetContents()
  333.         except BadZipfile:
  334.             if not self._filePassed:
  335.                 self.fp.close()
  336.                 self.fp = None
  337.             
  338.             raise 
  339.  
  340.  
  341.     
  342.     def _RealGetContents(self):
  343.         '''Read in the table of contents for the ZIP file.'''
  344.         fp = self.fp
  345.         endrec = _EndRecData(fp)
  346.         if not endrec:
  347.             raise BadZipfile, 'File is not a zip file'
  348.         
  349.         if self.debug > 1:
  350.             print endrec
  351.         
  352.         size_cd = endrec[5]
  353.         offset_cd = endrec[6]
  354.         self.comment = endrec[8]
  355.         if endrec[9] > ZIP64_LIMIT:
  356.             x = endrec[9] - size_cd - 56 - 20
  357.         else:
  358.             x = endrec[9] - size_cd
  359.         concat = x - offset_cd
  360.         if self.debug > 2:
  361.             print 'given, inferred, offset', offset_cd, x, concat
  362.         
  363.         self.start_dir = offset_cd + concat
  364.         fp.seek(self.start_dir, 0)
  365.         data = fp.read(size_cd)
  366.         fp = cStringIO.StringIO(data)
  367.         total = 0
  368.         while total < size_cd:
  369.             centdir = fp.read(46)
  370.             total = total + 46
  371.             if centdir[0:4] != stringCentralDir:
  372.                 raise BadZipfile, 'Bad magic number for central directory'
  373.             
  374.             centdir = struct.unpack(structCentralDir, centdir)
  375.             if self.debug > 2:
  376.                 print centdir
  377.             
  378.             filename = fp.read(centdir[_CD_FILENAME_LENGTH])
  379.             x = ZipInfo(filename)
  380.             x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
  381.             x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
  382.             total = total + centdir[_CD_FILENAME_LENGTH] + centdir[_CD_EXTRA_FIELD_LENGTH] + centdir[_CD_COMMENT_LENGTH]
  383.             x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET]
  384.             (x.create_version, x.create_system, x.extract_version, x.reserved, x.flag_bits, x.compress_type, t, d, x.CRC, x.compress_size, x.file_size) = centdir[1:12]
  385.             (x.volume, x.internal_attr, x.external_attr) = centdir[15:18]
  386.             x.date_time = ((d >> 9) + 1980, d >> 5 & 15, d & 31, t >> 11, t >> 5 & 63, (t & 31) * 2)
  387.             x._decodeExtra()
  388.             x.header_offset = x.header_offset + concat
  389.             self.filelist.append(x)
  390.             self.NameToInfo[x.filename] = x
  391.             if self.debug > 2:
  392.                 print 'total', total
  393.                 continue
  394.  
  395.     
  396.     def namelist(self):
  397.         '''Return a list of file names in the archive.'''
  398.         l = []
  399.         for data in self.filelist:
  400.             l.append(data.filename)
  401.         
  402.         return l
  403.  
  404.     
  405.     def infolist(self):
  406.         '''Return a list of class ZipInfo instances for files in the
  407.         archive.'''
  408.         return self.filelist
  409.  
  410.     
  411.     def printdir(self):
  412.         '''Print a table of contents for the zip file.'''
  413.         print '%-46s %19s %12s' % ('File Name', 'Modified    ', 'Size')
  414.         for zinfo in self.filelist:
  415.             date = '%d-%02d-%02d %02d:%02d:%02d' % zinfo.date_time[:6]
  416.             print '%-46s %s %12d' % (zinfo.filename, date, zinfo.file_size)
  417.         
  418.  
  419.     
  420.     def testzip(self):
  421.         '''Read all the files and check the CRC.'''
  422.         for zinfo in self.filelist:
  423.             
  424.             try:
  425.                 self.read(zinfo.filename)
  426.             continue
  427.             except BadZipfile:
  428.                 return zinfo.filename
  429.                 continue
  430.             
  431.  
  432.         
  433.  
  434.     
  435.     def getinfo(self, name):
  436.         """Return the instance of ZipInfo given 'name'."""
  437.         return self.NameToInfo[name]
  438.  
  439.     
  440.     def read(self, name):
  441.         '''Return file bytes (as a string) for name.'''
  442.         if self.mode not in ('r', 'a'):
  443.             raise RuntimeError, 'read() requires mode "r" or "a"'
  444.         
  445.         if not self.fp:
  446.             raise RuntimeError, 'Attempt to read ZIP archive that was already closed'
  447.         
  448.         zinfo = self.getinfo(name)
  449.         filepos = self.fp.tell()
  450.         self.fp.seek(zinfo.header_offset, 0)
  451.         fheader = self.fp.read(30)
  452.         if fheader[0:4] != stringFileHeader:
  453.             raise BadZipfile, 'Bad magic number for file header'
  454.         
  455.         fheader = struct.unpack(structFileHeader, fheader)
  456.         fname = self.fp.read(fheader[_FH_FILENAME_LENGTH])
  457.         if fheader[_FH_EXTRA_FIELD_LENGTH]:
  458.             self.fp.read(fheader[_FH_EXTRA_FIELD_LENGTH])
  459.         
  460.         if fname != zinfo.orig_filename:
  461.             raise BadZipfile, 'File name in directory "%s" and header "%s" differ.' % (zinfo.orig_filename, fname)
  462.         
  463.         bytes = self.fp.read(zinfo.compress_size)
  464.         self.fp.seek(filepos, 0)
  465.         if zinfo.compress_type == ZIP_STORED:
  466.             pass
  467.         elif zinfo.compress_type == ZIP_DEFLATED:
  468.             if not zlib:
  469.                 raise RuntimeError, 'De-compression requires the (missing) zlib module'
  470.             
  471.             dc = zlib.decompressobj(-15)
  472.             bytes = dc.decompress(bytes)
  473.             ex = dc.decompress('Z') + dc.flush()
  474.             if ex:
  475.                 bytes = bytes + ex
  476.             
  477.         else:
  478.             raise BadZipfile, 'Unsupported compression method %d for file %s' % (zinfo.compress_type, name)
  479.         crc = binascii.crc32(bytes)
  480.         if crc != zinfo.CRC:
  481.             raise BadZipfile, 'Bad CRC-32 for file %s' % name
  482.         
  483.         return bytes
  484.  
  485.     
  486.     def _writecheck(self, zinfo):
  487.         '''Check for errors before writing a file to the archive.'''
  488.         if zinfo.filename in self.NameToInfo:
  489.             if self.debug:
  490.                 print 'Duplicate name:', zinfo.filename
  491.             
  492.         
  493.         if self.mode not in ('w', 'a'):
  494.             raise RuntimeError, 'write() requires mode "w" or "a"'
  495.         
  496.         if not self.fp:
  497.             raise RuntimeError, 'Attempt to write ZIP archive that was already closed'
  498.         
  499.         if zinfo.compress_type == ZIP_DEFLATED and not zlib:
  500.             raise RuntimeError, 'Compression requires the (missing) zlib module'
  501.         
  502.         if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
  503.             raise RuntimeError, 'That compression method is not supported'
  504.         
  505.         if zinfo.file_size > ZIP64_LIMIT:
  506.             if not self._allowZip64:
  507.                 raise LargeZipFile('Filesize would require ZIP64 extensions')
  508.             
  509.         
  510.         if zinfo.header_offset > ZIP64_LIMIT:
  511.             if not self._allowZip64:
  512.                 raise LargeZipFile('Zipfile size would require ZIP64 extensions')
  513.             
  514.         
  515.  
  516.     
  517.     def write(self, filename, arcname = None, compress_type = None):
  518.         '''Put the bytes from filename into the archive under the name
  519.         arcname.'''
  520.         st = os.stat(filename)
  521.         mtime = time.localtime(st.st_mtime)
  522.         date_time = mtime[0:6]
  523.         if arcname is None:
  524.             arcname = filename
  525.         
  526.         arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
  527.         while arcname[0] in (os.sep, os.altsep):
  528.             arcname = arcname[1:]
  529.         zinfo = ZipInfo(arcname, date_time)
  530.         zinfo.external_attr = (st[0] & 65535) << 0x10L
  531.         if compress_type is None:
  532.             zinfo.compress_type = self.compression
  533.         else:
  534.             zinfo.compress_type = compress_type
  535.         zinfo.file_size = st.st_size
  536.         zinfo.flag_bits = 0
  537.         zinfo.header_offset = self.fp.tell()
  538.         self._writecheck(zinfo)
  539.         self._didModify = True
  540.         fp = open(filename, 'rb')
  541.         zinfo.CRC = CRC = 0
  542.         zinfo.compress_size = compress_size = 0
  543.         zinfo.file_size = file_size = 0
  544.         self.fp.write(zinfo.FileHeader())
  545.         if zinfo.compress_type == ZIP_DEFLATED:
  546.             cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15)
  547.         else:
  548.             cmpr = None
  549.         while None:
  550.             buf = fp.read(8192)
  551.             if not buf:
  552.                 break
  553.             
  554.             file_size = file_size + len(buf)
  555.             CRC = binascii.crc32(buf, CRC)
  556.             if cmpr:
  557.                 buf = cmpr.compress(buf)
  558.                 compress_size = compress_size + len(buf)
  559.             
  560.             continue
  561.             fp.close()
  562.             if cmpr:
  563.                 buf = cmpr.flush()
  564.                 compress_size = compress_size + len(buf)
  565.                 self.fp.write(buf)
  566.                 zinfo.compress_size = compress_size
  567.             else:
  568.                 zinfo.compress_size = file_size
  569.         zinfo.CRC = CRC
  570.         zinfo.file_size = file_size
  571.         position = self.fp.tell()
  572.         self.fp.seek(zinfo.header_offset + 14, 0)
  573.         self.fp.write(struct.pack('<lLL', zinfo.CRC, zinfo.compress_size, zinfo.file_size))
  574.         self.fp.seek(position, 0)
  575.         self.filelist.append(zinfo)
  576.         self.NameToInfo[zinfo.filename] = zinfo
  577.  
  578.     
  579.     def writestr(self, zinfo_or_arcname, bytes):
  580.         """Write a file into the archive.  The contents is the string
  581.         'bytes'.  'zinfo_or_arcname' is either a ZipInfo instance or
  582.         the name of the file in the archive."""
  583.         if not isinstance(zinfo_or_arcname, ZipInfo):
  584.             zinfo = ZipInfo(filename = zinfo_or_arcname, date_time = time.localtime(time.time())[:6])
  585.             zinfo.compress_type = self.compression
  586.         else:
  587.             zinfo = zinfo_or_arcname
  588.         zinfo.file_size = len(bytes)
  589.         zinfo.header_offset = self.fp.tell()
  590.         self._writecheck(zinfo)
  591.         self._didModify = True
  592.         zinfo.CRC = binascii.crc32(bytes)
  593.         if zinfo.compress_type == ZIP_DEFLATED:
  594.             co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15)
  595.             bytes = co.compress(bytes) + co.flush()
  596.             zinfo.compress_size = len(bytes)
  597.         else:
  598.             zinfo.compress_size = zinfo.file_size
  599.         zinfo.header_offset = self.fp.tell()
  600.         self.fp.write(zinfo.FileHeader())
  601.         self.fp.write(bytes)
  602.         self.fp.flush()
  603.         if zinfo.flag_bits & 8:
  604.             self.fp.write(struct.pack('<lLL', zinfo.CRC, zinfo.compress_size, zinfo.file_size))
  605.         
  606.         self.filelist.append(zinfo)
  607.         self.NameToInfo[zinfo.filename] = zinfo
  608.  
  609.     
  610.     def __del__(self):
  611.         '''Call the "close()" method in case the user forgot.'''
  612.         self.close()
  613.  
  614.     
  615.     def close(self):
  616.         '''Close the file, and for mode "w" and "a" write the ending
  617.         records.'''
  618.         if self.fp is None:
  619.             return None
  620.         
  621.         if self.mode in ('w', 'a') and self._didModify:
  622.             count = 0
  623.             pos1 = self.fp.tell()
  624.             for zinfo in self.filelist:
  625.                 count = count + 1
  626.                 dt = zinfo.date_time
  627.                 dosdate = dt[0] - 1980 << 9 | dt[1] << 5 | dt[2]
  628.                 dostime = dt[3] << 11 | dt[4] << 5 | dt[5] // 2
  629.                 extra = []
  630.                 if zinfo.file_size > ZIP64_LIMIT or zinfo.compress_size > ZIP64_LIMIT:
  631.                     extra.append(zinfo.file_size)
  632.                     extra.append(zinfo.compress_size)
  633.                     file_size = 0xFFFFFFFFL
  634.                     compress_size = 0xFFFFFFFFL
  635.                 else:
  636.                     file_size = zinfo.file_size
  637.                     compress_size = zinfo.compress_size
  638.                 if zinfo.header_offset > ZIP64_LIMIT:
  639.                     extra.append(zinfo.header_offset)
  640.                     header_offset = -1
  641.                 else:
  642.                     header_offset = zinfo.header_offset
  643.                 extra_data = zinfo.extra
  644.                 if extra:
  645.                     extra_data = struct.pack('<hh' + 'q' * len(extra), 1, 8 * len(extra), *extra) + extra_data
  646.                     extract_version = max(45, zinfo.extract_version)
  647.                     create_version = max(45, zinfo.create_version)
  648.                 else:
  649.                     extract_version = zinfo.extract_version
  650.                     create_version = zinfo.create_version
  651.                 centdir = struct.pack(structCentralDir, stringCentralDir, create_version, zinfo.create_system, extract_version, zinfo.reserved, zinfo.flag_bits, zinfo.compress_type, dostime, dosdate, zinfo.CRC, compress_size, file_size, len(zinfo.filename), len(extra_data), len(zinfo.comment), 0, zinfo.internal_attr, zinfo.external_attr, header_offset)
  652.                 self.fp.write(centdir)
  653.                 self.fp.write(zinfo.filename)
  654.                 self.fp.write(extra_data)
  655.                 self.fp.write(zinfo.comment)
  656.             
  657.             pos2 = self.fp.tell()
  658.             if pos1 > ZIP64_LIMIT:
  659.                 zip64endrec = struct.pack(structEndArchive64, stringEndArchive64, 44, 45, 45, 0, 0, count, count, pos2 - pos1, pos1)
  660.                 self.fp.write(zip64endrec)
  661.                 zip64locrec = struct.pack(structEndArchive64Locator, stringEndArchive64Locator, 0, pos2, 1)
  662.                 self.fp.write(zip64locrec)
  663.                 pos3 = self.fp.tell()
  664.                 endrec = struct.pack(structEndArchive, stringEndArchive, 0, 0, count, count, pos2 - pos1, -1, 0)
  665.                 self.fp.write(endrec)
  666.             else:
  667.                 endrec = struct.pack(structEndArchive, stringEndArchive, 0, 0, count, count, pos2 - pos1, pos1, 0)
  668.                 self.fp.write(endrec)
  669.             self.fp.flush()
  670.         
  671.         if not self._filePassed:
  672.             self.fp.close()
  673.         
  674.         self.fp = None
  675.  
  676.  
  677.  
  678. class PyZipFile(ZipFile):
  679.     '''Class to create ZIP archives with Python library files and packages.'''
  680.     
  681.     def writepy(self, pathname, basename = ''):
  682.         '''Add all files from "pathname" to the ZIP archive.
  683.  
  684.         If pathname is a package directory, search the directory and
  685.         all package subdirectories recursively for all *.py and enter
  686.         the modules into the archive.  If pathname is a plain
  687.         directory, listdir *.py and enter all modules.  Else, pathname
  688.         must be a Python *.py file and the module will be put into the
  689.         archive.  Added modules are always module.pyo or module.pyc.
  690.         This method will compile the module.py into module.pyc if
  691.         necessary.
  692.         '''
  693.         (dir, name) = os.path.split(pathname)
  694.         if os.path.isdir(pathname):
  695.             initname = os.path.join(pathname, '__init__.py')
  696.             if os.path.isfile(initname):
  697.                 if basename:
  698.                     basename = '%s/%s' % (basename, name)
  699.                 else:
  700.                     basename = name
  701.                 if self.debug:
  702.                     print 'Adding package in', pathname, 'as', basename
  703.                 
  704.                 (fname, arcname) = self._get_codename(initname[0:-3], basename)
  705.                 if self.debug:
  706.                     print 'Adding', arcname
  707.                 
  708.                 self.write(fname, arcname)
  709.                 dirlist = os.listdir(pathname)
  710.                 dirlist.remove('__init__.py')
  711.                 for filename in dirlist:
  712.                     path = os.path.join(pathname, filename)
  713.                     (root, ext) = os.path.splitext(filename)
  714.                     if os.path.isdir(path):
  715.                         if os.path.isfile(os.path.join(path, '__init__.py')):
  716.                             self.writepy(path, basename)
  717.                         
  718.                     os.path.isfile(os.path.join(path, '__init__.py'))
  719.                     if ext == '.py':
  720.                         (fname, arcname) = self._get_codename(path[0:-3], basename)
  721.                         if self.debug:
  722.                             print 'Adding', arcname
  723.                         
  724.                         self.write(fname, arcname)
  725.                         continue
  726.                 
  727.             elif self.debug:
  728.                 print 'Adding files from directory', pathname
  729.             
  730.             for filename in os.listdir(pathname):
  731.                 path = os.path.join(pathname, filename)
  732.                 (root, ext) = os.path.splitext(filename)
  733.                 if ext == '.py':
  734.                     (fname, arcname) = self._get_codename(path[0:-3], basename)
  735.                     if self.debug:
  736.                         print 'Adding', arcname
  737.                     
  738.                     self.write(fname, arcname)
  739.                     continue
  740.             
  741.         elif pathname[-3:] != '.py':
  742.             raise RuntimeError, 'Files added with writepy() must end with ".py"'
  743.         
  744.         (fname, arcname) = self._get_codename(pathname[0:-3], basename)
  745.         if self.debug:
  746.             print 'Adding file', arcname
  747.         
  748.         self.write(fname, arcname)
  749.  
  750.     
  751.     def _get_codename(self, pathname, basename):
  752.         '''Return (filename, archivename) for the path.
  753.  
  754.         Given a module name path, return the correct file path and
  755.         archive name, compiling if necessary.  For example, given
  756.         /python/lib/string, return (/python/lib/string.pyc, string).
  757.         '''
  758.         file_py = pathname + '.py'
  759.         file_pyc = pathname + '.pyc'
  760.         file_pyo = pathname + '.pyo'
  761.         if os.path.isfile(file_pyo) and os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime:
  762.             fname = file_pyo
  763.         elif not os.path.isfile(file_pyc) or os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime:
  764.             import py_compile as py_compile
  765.             if self.debug:
  766.                 print 'Compiling', file_py
  767.             
  768.             
  769.             try:
  770.                 py_compile.compile(file_py, file_pyc, None, True)
  771.             except py_compile.PyCompileError:
  772.                 err = None
  773.                 print err.msg
  774.  
  775.             fname = file_pyc
  776.         else:
  777.             fname = file_pyc
  778.         archivename = os.path.split(fname)[1]
  779.         if basename:
  780.             archivename = '%s/%s' % (basename, archivename)
  781.         
  782.         return (fname, archivename)
  783.  
  784.  
  785.  
  786. def main(args = None):
  787.     import textwrap as textwrap
  788.     USAGE = textwrap.dedent('        Usage:\n            zipfile.py -l zipfile.zip        # Show listing of a zipfile\n            zipfile.py -t zipfile.zip        # Test if a zipfile is valid\n            zipfile.py -e zipfile.zip target # Extract zipfile into target dir\n            zipfile.py -c zipfile.zip src ... # Create zipfile from sources\n        ')
  789.     if args is None:
  790.         args = sys.argv[1:]
  791.     
  792.     if not args or args[0] not in ('-l', '-c', '-e', '-t'):
  793.         print USAGE
  794.         sys.exit(1)
  795.     
  796.     if args[0] == '-l':
  797.         if len(args) != 2:
  798.             print USAGE
  799.             sys.exit(1)
  800.         
  801.         zf = ZipFile(args[1], 'r')
  802.         zf.printdir()
  803.         zf.close()
  804.     elif args[0] == '-t':
  805.         if len(args) != 2:
  806.             print USAGE
  807.             sys.exit(1)
  808.         
  809.         zf = ZipFile(args[1], 'r')
  810.         zf.testzip()
  811.         print 'Done testing'
  812.     elif args[0] == '-e':
  813.         if len(args) != 3:
  814.             print USAGE
  815.             sys.exit(1)
  816.         
  817.         zf = ZipFile(args[1], 'r')
  818.         out = args[2]
  819.         for path in zf.namelist():
  820.             if path.startswith('./'):
  821.                 tgt = os.path.join(out, path[2:])
  822.             else:
  823.                 tgt = os.path.join(out, path)
  824.             tgtdir = os.path.dirname(tgt)
  825.             if not os.path.exists(tgtdir):
  826.                 os.makedirs(tgtdir)
  827.             
  828.             fp = open(tgt, 'wb')
  829.             fp.write(zf.read(path))
  830.             fp.close()
  831.         
  832.         zf.close()
  833.     elif args[0] == '-c':
  834.         if len(args) < 3:
  835.             print USAGE
  836.             sys.exit(1)
  837.         
  838.         
  839.         def addToZip(zf, path, zippath):
  840.             if os.path.isfile(path):
  841.                 zf.write(path, zippath, ZIP_DEFLATED)
  842.             elif os.path.isdir(path):
  843.                 for nm in os.listdir(path):
  844.                     addToZip(zf, os.path.join(path, nm), os.path.join(zippath, nm))
  845.                 
  846.             
  847.  
  848.         zf = ZipFile(args[1], 'w', allowZip64 = True)
  849.         for src in args[2:]:
  850.             addToZip(zf, src, os.path.basename(src))
  851.         
  852.         zf.close()
  853.     
  854.  
  855. if __name__ == '__main__':
  856.     main()
  857.  
  858.